A multi-client content platform for Digital Allies. One workspace runs digitalallies.net and every client site after it — pages, articles, projects, and research, gated by subscription plan, all reading from a single connected source.
Not a copy of the site — the source of it.
The admin and the public site are not two systems kept in sync. They read and write the same store. An edit in the workspace is the site changing. The prototype proves the loop end-to-end; production swaps the in-memory store for a database and the preview iframe for the real deployment.
Public-facing areas carry brand names; internal tools stay plainly named.
Per-client overview — pages, drafts, open tasks, unread transmissions. The landing screen on every login.
The heart. Page builder, articles (The Journal), Departments, Field Notes, the Command Center, and site settings — folded into one tabbed module. Everything that publishes to the site lives here.
Kanban boards per client. Drag tasks across columns, inline-edit titles, cycle priority, set due dates. The build tracker for client work.
Notebook archive — tagged notes, search, export. The same connected store pattern, scoped to internal knowledge rather than published content.
Development tracker — features, bugs, and launch milestones logged against each site. The engineering counterpart to Projects, reserved for the full-service Agency tier.
Plan lives on the client record. The nav locks anything the plan doesn't include and routes to an in-context upgrade screen.
| Module | Starter $0 | Studio From $49/mo | Agency From $149/mo |
|---|---|---|---|
| Dashboard | ● | ● | ● |
| The Press Office | ● | ● | ● |
| Projects | — | ● | ● |
| Research | — | ● | ● |
| The Workshop | — | — | ● |
| Multi-client + white-label | — | — | ● |
Gating is a single helper: can(client, module) checks the plan's module list. Drives the nav lock icon, the route guard, and the upsell screen — one source of truth, no scattered checks.
You at the top; each client a tenant with its own site, plan, and brand.
Every client is a self-contained record: identity, domain, plan, brand color, and a site object holding all its content. The client switcher in the top bar swaps the active tenant; nothing else in the UI changes. Digital Allies is simply the first client (flagged owner, on Agency).
Content, projects, and messages never cross tenants. In production this maps to a client_id foreign key on every row and row-level security — the same boundary the prototype enforces by scoping all mutations to the active client.
One shape per client. The site object is what the public renderer consumes.
// client { id, name, domain, initials, brand_color, plan: 'starter' | 'studio' | 'agency', live_url, admin_url, site: { settings: { site_title, tagline, hero_*, about_*, phone, email, … }, pages: [ { id, title, slug, status, blocks: [ … ] } ], articles: [ { id, title, slug, excerpt, content, status, … } ], departments: [ { id, title, icon, price, description, order } ], fieldNotes: [ { id, author_name, author_role, rating, content } ], messages: [ { id, name, email, subject, message, read, … } ], projects: [ { id, name, columns, tasks: [ … ] } ], } } // page block — the page builder unit { id, type: 'hero'|'richtext'|'departments'|'fieldNotes'|'cta', data: { … } }
Blocks render in both the builder and the public site from the same array — reorder is a splice, publish is a status flag. Departments and Field Notes blocks pull live from their own collections so they're never duplicated.
digitalallies.net + the existing admin panel, linked in context.
The workspace's View live site opens the real deployment inline, with a toggle between the public site and the existing admin panel and a one-click open-in-tab. In production the published site reads the same store the workspace writes, so "preview" and "live" converge.
A suggested Next.js layout — admin and public site in one deployment.
digital-allies-cms/
app/
(admin)/ # the workspace — auth-gated
dashboard/
content/ # Press Office: pages, articles, …
projects/ # Studio+
research/ # Studio+
workshop/ # Agency
(site)/[client]/ # public renderer, per domain
api/ # store reads/writes, auth, billing
lib/
store.ts # data access (swap for Postgres)
plans.ts # tiers + can() gating helper
blocks/ # block type registry + renderers
components/ # shared UI (lifted from prototype)
middleware.ts # tenant + plan routing
The prototype maps almost 1:1: store.js → lib/store.ts, the module components → route folders, plans → lib/plans.ts. Billing (Stripe) and auth slot into api/.
Ship the connected loop first; layer modules and billing after.
can() gating, lock states, upgrade screensThis plan is the map, not the contract. Phase 1 stands on its own — a connected CMS running the real site. Everything after it is additive, gated, and sellable. No surprises, no silent scope creep.